package Scuttlebutt;

import java.awt.*;
import java.awt.event.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;
import java.awt.*;
import java.io.*;
import java.util.*;
import javax.swing.plaf.ProgressBarUI;
import glue.ftp.*;



class Download extends Observable implements Runnable, FtpObserver
{

	protected Thread thisThread = null;
	SortableNode node = null;
	public String dest;

	protected float progress;
	protected float count;
	File file = null;
	Scuttlebutt parent = null;
	
	boolean uploading = false;
	boolean connected = false;
	
	String address = null;
	String name = null;
	String password = null;
	Thread thread = null;
	
	String homeDirectory = null;
	
	FtpBean ftp = null;
	
	
	public Download(){
	}
	
	public Download(Scuttlebutt parent, SortableNode node)
	{
		this.parent = parent;
		ftp = new FtpBean();
		this.address = parent.getProject().ftpAddress;
		this.name = parent.getProject().getUsername();
		this.password = parent.getProject().getPassword();
		
		this.node = node;

		this.dest = node.toString();
		this.homeDirectory = parent.getRemoteHomeDirectory();
		file = new File(node.getNodePath());
		if(file.length() == 0){
			progress = 100.0f;
		}else{
			progress = 0;
		}
		
		count = node.getProgress();
		
		Tab t = parent.getEditPane().search(file.getPath());
		if(t != null){
			t.setEnabled(false);
		}
		parent.getEditPane().setEnabledAt(dest, false);
	}

	public void setConnectedState(boolean state){
		connected = state;
	}
	
	
	public void setProgress( float progress){
		this.progress= progress;
	}
	
	public boolean canAddConnection(){
		return parent.getFTP().canAddConnection();	
	}

	public void sleep(int t){
		try{
			thread.sleep(t);
		}catch(java.lang.InterruptedException e){
			System.err.println(e);
		}
	}
	
	public boolean Connect(){
		try{
			connected = ftp.ftpConnect(address, name, password);
		}catch(Exception e){
			connected = false;
			System.err.println("Download failed to connect:" + e);
		}
		if(connected){
			try{
				if(node.getRemoteDirectory().compareToIgnoreCase("") != 0){
					ftp.setDirectory(node.getRemoteDirectory());
				}
				FtpListResult result = ftp.getDirectoryContent();
				if(node.getProgress() > 0.0){
			        while(result.next()){
				        if(result.getName().compareToIgnoreCase(node.toString()) == 0){
					        count = result.getSize();
					        return connected;
				        }
			        }
				}
				
			}catch(java.io.IOException e){
				System.err.println("Couldn't set directory:" + e);
			}catch(FtpException e){
				connected = false;
				System.err.println("Couldn't set directory:" + e);
				return connected;
			}
		}else{
			System.out.println("failed to connect:" + name);
		}
		return connected;
	}
	
	public void Start(){
		uploading = true;
		node.setUploadingState(true);
		new Thread(this).start();
		
	}

	public String getFilename() { return file.getPath(); }
	public int getFilesize() { return (int)file.length(); }
	public float getProgress() { return progress; }

	public String toString()
	{
		return "[" + file.getPath() + ", " + file.length() + ", " + progress + "]"; 
	}
	
	public boolean isUploading(){
		return uploading;
	}
	
	public void run()
	{
		try{
			if(file.length() == 0){
				
				try{
					FileWriter b = new FileWriter(file);
					BufferedWriter w = new BufferedWriter(b);
					String buffer = new String(" ");
					w.write(buffer, 0, buffer.length());
					w.close();
					b.close();
					
					w = null;
					b = null;
		
				}catch(IOException io){
					System.err.print(io.getMessage());
				}
			}
			ftp.putBinaryFile(file.getPath(), file.getName(), (long)count, this);

		}catch(java.io.IOException e){
				System.err.println(e);
		}catch(FtpException f){
				System.err.println("failed to put file:" + f);
		}
	}
	
	public boolean getConnectedState(){
		return connected;
	}
	
	public boolean getUploadingState(){
		return uploading;
	}
	
	public void byteRead(int i0)
	{
	}

	public void byteWrite(int i0)
	{
		count += i0;
		node.setProgress(count);
		progress = (count/(int)file.length()) * 100;
		update();
		setChanged();
		notifyObservers(this);
	}
	
	public void update(){
		if(progress >= 100.0){
			uploading = false;
			node.setUploadingState(uploading);
			node.setUploaded(true);
			node.setProgress(0);
			count = 0;
			Tab t = parent.getEditPane().search(file.getPath());
			if(t != null){
				t.setEnabled(true);
			}
			parent.getEditPane().setEnabledAt(dest, true);
		}
	}
}

/*

class WaitThread extends Thread{	
	DownloadTableModel model = null;
	public WaitThread(DownloadTableModel model){
		this.model = model;
	}
	
	public void ConnectNext(){
		model.ConnectNext();
	}
	
	public void run(){
		try{
		sleep(30000);
		}catch(java.lang.InterruptedException e){
		}
		ConnectNext();
	}
}

*/
class DownloadTableModel extends AbstractTableModel implements Observer
{
	// holds the strings to be displayed in the column headers of our table
	final String[] columnNames = {"Filename", "Filesize", "Progress"};

	// holds the data types for all our columns
	final Class[] columnClasses = {String.class, Integer.class, JProgressBar.class};

	// holds our data
	final Vector data = new Vector();
	
	int connected = 0;
	static int maxConnections = 0;
	static boolean maxConnectionsReached = false;
	static int currentConnections = 0;
	Scuttlebutt parent = null;
	
	public void setParent(Scuttlebutt parent){
		this.parent = parent;
	}
	// adds a row
	public void addDownload(Download d)
	{
		if(!isFileDownloading(d.getFilename()))
			data.addElement(d);

		// the table model is interested in changes of the rows
		d.addObserver(this);
		fireTableRowsInserted(data.size()-1, data.size()-1);
	}
	
	public boolean isFileDownloading(String path){
		for (Enumeration e = data.elements() ; e.hasMoreElements() ;) {
			Download d = (Download)e.nextElement();
			if(path.compareToIgnoreCase(d.getFilename()) == 0)
				return d.isUploading();
		}
		return false;
	}
	
	private int findNotConnected(){
		for(int x = 0; x < getRowCount(); x++){
			Download d = (Download)data.get(x);
			if(!d.getConnectedState())
				return x;
		}
		return -1;
	}

	// is called by a download object when its state changes
	public void update(Observable observable, Object o)
	{
		int index = data.indexOf(o);
		if (index != -1)
		fireTableRowsUpdated(index, index);
		Download d = (Download)data.get(index);
		if((d.getProgress() >= 100.0) && (getRowCount() > connected)){		
			currentConnections--;
			parent.updateTree();
		}
	}

	public boolean canAddConnection(){
		if(!maxConnectionsReached){
			return true;
		}else{
			if((currentConnections < maxConnections)){
				return true;
			}else{
				return false;
			}
		}
	}

	private void Connect(){
		for (Enumeration e = data.elements() ; e.hasMoreElements() ;) {
			Download d = (Download)e.nextElement();
			if(canAddConnection()){
				if(d.Connect()){
					connected++;
					currentConnections++;
					maxConnections++;
				}else{
					maxConnectionsReached = true;
				}
			}
		}
	}
	
	public void ConnectNext(){
			int idx = findNotConnected();
			if(idx == -1){
				return;
			}
			Download temp = (Download)data.get(idx);
			if(temp.Connect()){
				connected++;
				currentConnections++;
			}
	}
	
	public void startDownloading(){
		for (Enumeration e = data.elements() ; e.hasMoreElements() ;) {
			Download d = (Download)e.nextElement();
			if(!d.getConnectedState()){
				d.Connect();
				d.Start();
			}
		}
	}
	
	public boolean canRemove(int idx){
		Download d = (Download)data.get(idx);
		return (d.isUploading()) ? false : true;
	}
	
	public void remove(int idx){
		if(canRemove(idx))
			data.remove(idx);
	}
	
	public void clear(){
		data.clear();
	}

	public int getColumnCount()
	{
		return columnNames.length;
	}

	public int getRowCount()
	{
		return data.size();
	}

	public String getColumnName(int col)
	{
		return columnNames[col];
	}

	public Class getColumnClass(int c)
	{
		return columnClasses[c];
	}

	public Object getValueAt(int row, int col)
	{	 
		Download download = (Download) data.elementAt(row);
		if (col == 0)
			return download.getFilename();
		else if (col == 1)
			return new Integer(download.getFilesize());
		else if (col == 2)
			return new Float(download.getProgress());
		else
			return null;
	}

	public boolean isCellEditable(int row, int col)
	{
		return false;
	}
}

class SocketProgressBar extends JProgressBar implements TableCellRenderer 
{
	public SocketProgressBar()
	{
		super();
	}

	public SocketProgressBar(BoundedRangeModel newModel)
	{
		super(newModel);
	}

	public SocketProgressBar(int orient)
	{
		super(orient);
	}

	public SocketProgressBar(int min, int max)
	{
		super(min, max);
	}

	public SocketProgressBar(int orient, int min, int max)
	{
		super(orient, min, max);
	}

	public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,int row, int column)
	{
		setValue((int) ((Float) value).floatValue());
		return this;
	}
}

public class FileTransferTable extends JTable
{
	
	Scuttlebutt parent = null;
	DownloadTableModel model = null;
	
	public FileTransferTable(Scuttlebutt parent)
	{
		super(new DownloadTableModel());
		this.parent = parent;
		// add rows to our TableModel, each row is represented as a Download object
		model = (DownloadTableModel)getModel();
		model.setParent(parent);
		addMouseListener(new TableMouseEventHandler(parent));
		setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		// render the columns with class JProgressBar as such
		SocketProgressBar pbr = new SocketProgressBar(0, 100);
		pbr.setStringPainted(true);
		setDefaultRenderer(JProgressBar.class, pbr);

		// increase the height of the rows a bit
		setRowHeight((int) pbr.getPreferredSize().getHeight());
		
	}
	
	public void startDownloading(){
		model.startDownloading();
	}
	
 	
	public void addDownload(Download d){
		if(!isFileDownloading(d.getFilename()))
			model.addDownload(d);
	}
	
	public void clear(){
		model.clear();
		updateUI();
	}
	
	public boolean isFileDownloading(String path){
		for(int x = 0; x < model.getRowCount(); x++){
			String upload = (String)getValueAt(x, 0);
			if(upload.compareToIgnoreCase(path) == 0){
				return true;
			}
		}
		return false;
	}
	
  
    
	public class TableMouseEventHandler implements MouseListener 
	{
		Scuttlebutt parent = null;
		
		public TableMouseEventHandler(Scuttlebutt parent)
		{
			this.parent = parent;
		}
	
		public void mouseClicked(MouseEvent m0)
		{
			if(m0.getButton() == m0.BUTTON1){
			}else if(m0.getButton() == m0.BUTTON2){
			}else if(m0.getButton() == m0.BUTTON3){
				FileTransferTable table = (FileTransferTable)m0.getComponent();
				table.setRowSelectionInterval(table.rowAtPoint(new Point(m0.getX(), m0.getY())), table.rowAtPoint(new Point(m0.getX(), m0.getY())));
				DownloadTableModel m = (DownloadTableModel)((FileTransferTable)m0.getComponent()).getModel();
				FileTransferTablePopupMenu pop = new FileTransferTablePopupMenu(table, m0.getX(), m0.getY());
				pop.show(m0.getComponent(), m0.getX(), m0.getY());
			}
		}
	
		public void mouseEntered(MouseEvent m0)
		{
		}
	
		public void mouseExited(MouseEvent m0)
		{
		}
	
		public void mousePressed(MouseEvent m0)
		{
		}
	
		public void mouseReleased(MouseEvent m0)
		{
		}
	}     
}

